/*
 *  MidiMessage.cpp
 *  MusicAnalyzer
 *
 *  Created by jiro on 12/03/30.
 *  Copyright 2012 jiro music. All rights reserved.
 *
 */

#include "MidiMessage.h"

#include <sstream>

namespace sc
{
	std::string MidiMessage::GetFirstCategoryName( FirstCategory category )
	{
		switch ( category )
		{
		//	 first category
		case FirstCategory_Channel: return "FirstCategory_Channel";
		case FirstCategory_System: return "FirstCategory_System";
		}
		return "";
	}

	std::string MidiMessage::GetSecondCategoryName( SecondCategory category )
	{
		switch ( category )
		{
		//	channel category
		case SecondCategory_Voice: return "SecondCategory_Voice";
		case SecondCategory_Mode: return "SecondCategory_Mode";

		//	system category
		case SecondCategory_Exclusive: return "SecondCategory_Exclusive";
		case SecondCategory_Common: return "SecondCategory_Common";
		case SecondCategory_RealTime: return "SecondCategory_RealTime";
		}
		return "";
	}

	std::string MidiMessage::GetTypeName( Type type )
	{
		switch ( type ) 
		{
		//	voice category
		case Type_NoteOn: return "Type_NoteOn";
		case Type_NoteOff: return "Type_NoteOff";
		case Type_ChannelPressure: return "Type_ChannelPressure";
		case Type_ControlChange: return "Type_ControlChange";
		case Type_PitchBendChange: return "Type_PitchBendChange";
		case Type_PolyphonicKeyPressure: return "Type_PolyphonicKeyPressure";
		case Type_ProgramChange: return "Type_ProgramChange";

		//	mode category
		case Type_AllNoteOff: return "Type_AllNoteOff";
		case Type_AllSoundOff: return "Type_AllSoundOff";
		case Type_LocalControl: return "Type_LocalControl";
		case Type_PolyphonicOn: return "Type_PolyphonicOn";
		case Type_MonophonicOn: return "Type_MonophonicOn";
		case Type_OmniOn: return "Type_OmniOn";
		case Type_OmniOff: return "Type_OmniOff";
		case Type_ResetAllController: return "Type_ResetAllController";

		//	system category
		case Type_Exclusive: return "Type_Exclusive";

		//	common category
		case Type_EndOfExclusive: return "Type_EndOfExclusive";
		case Type_QuarterFrame: return "Type_QuarterFrame";
		case Type_SongPositionPointer: return "Type_SongPositionPointer";
		case Type_SongSelect: return "Type_SongSelect";
		case Type_TuneRequest: return "Type_TuneRequest";

		//	real time category
		case Type_ActiveSencing: return "Type_ActiveSencing";
		case Type_Continue: return "Type_Continue";
		case Type_Start: return "Type_Start";
		case Type_Stop: return "Type_Stop";
		case Type_SystemReset: return "Type_SystemReset";
		case Type_TimingClock: return "Type_TimingClock";
		}
		return "";
	}

	std::string MidiMessage::GetControlChangeTypeName( ControlChangeType type )
	{
		switch ( type )
		{
		case ControlChangeType_BankSelect: return "BankSelect";
		case ControlChangeType_ModulationWheelOrLever: return "ModulationWheelOrLever";
		case ControlChangeType_BreathController: return "BreathController";
		case ControlChangeType_FootController: return "FootController";
		case ControlChangeType_PortamentoTime: return "PortamentoTime";
		case ControlChangeType_ChannelVolume: return "ChannelVolume";
		case ControlChangeType_Balance: return "Balance";
		case ControlChangeType_Pan: return "Pan";
		case ControlChangeType_ExpressionController: return "ExpressionController";
		case ControlChangeType_EffectControl1: return "EffectControl1";
		case ControlChangeType_EffectControl2: return "EffectControl2";
		case ControlChangeType_GeneralPurposeController1: return "GeneralPurposeController1";
		case ControlChangeType_GeneralPurposeController2: return "GeneralPurposeController2";
		case ControlChangeType_GeneralPurposeController3: return "GeneralPurposeController3";
		case ControlChangeType_GeneralPurposeController4: return "GeneralPurposeController4";
		case ControlChangeType_LSBForControl0_BankSelect: return "LSBForControl0_BankSelect";
		case ControlChangeType_LSBForControl1_ModulationWheelOrLever: return "LSBForControl1_ModulationWheelOrLever";
		case ControlChangeType_LSBForControl2_BreathController: return "LSBForControl2_BreathController";
		case ControlChangeType_LSBForControl3_Undefined: return "LSBForControl3_Undefined";
		case ControlChangeType_LSBForControl4_FootController: return "LSBForControl4_FootController";
		case ControlChangeType_LSBForControl5_PortamentoTime: return "LSBForControl5_PortamentoTime";
		case ControlChangeType_LSBForControl6_DataEntry: return "LSBForControl6_DataEntry";
		case ControlChangeType_LSBForControl7_ChannelVolume: return "LSBForControl7_ChannelVolume";
		case ControlChangeType_LSBForControl8_Balance: return "LSBForControl8_Balance";
		case ControlChangeType_LSBForControl9_Undefined: return "LSBForControl9_Undefined";
		case ControlChangeType_LSBForControl10_Pan: return "LSBForControl10_Pan";
		case ControlChangeType_LSBForControl11_ExpressionController: return "LSBForControl11_ExpressionController";
		case ControlChangeType_LSBForControl12_EffectControl1: return "LSBForControl12_EffectControl1";
		case ControlChangeType_LSBForControl13_EffectControl2: return "LSBForControl13_EffectControl2";
		case ControlChangeType_LSBForControl14_Undefined: return "LSBForControl14_Undefined";
		case ControlChangeType_LSBForControl15_Undefined: return "LSBForControl15_Undefined";
		case ControlChangeType_LSBForControl16_GeneralPurposeController1: return "LSBForControl16_GeneralPurposeController1";
		case ControlChangeType_LSBForControl17_GeneralPurposeController2: return "LSBForControl17_GeneralPurposeController2";
		case ControlChangeType_LSBForControl18_GeneralPurposeController3: return "LSBForControl18_GeneralPurposeController3";
		case ControlChangeType_LSBForControl19_GeneralPurposeController4: return "LSBForControl19_GeneralPurposeController4";
		case ControlChangeType_LSBForControl20_Undefined: return "LSBForControl20_Undefined";
		case ControlChangeType_LSBForControl21_Undefined: return "LSBForControl21_Undefined";
		case ControlChangeType_LSBForControl22_Undefined: return "LSBForControl22_Undefined";
		case ControlChangeType_LSBForControl23_Undefined: return "LSBForControl23_Undefined";
		case ControlChangeType_LSBForControl24_Undefined: return "LSBForControl24_Undefined";
		case ControlChangeType_LSBForControl25_Undefined: return "LSBForControl25_Undefined";
		case ControlChangeType_LSBForControl26_Undefined: return "LSBForControl26_Undefined";
		case ControlChangeType_LSBForControl27_Undefined: return "LSBForControl27_Undefined";
		case ControlChangeType_LSBForControl28_Undefined: return "LSBForControl28_Undefined";
		case ControlChangeType_LSBForControl29_Undefined: return "LSBForControl29_Undefined";
		case ControlChangeType_LSBForControl30_Undefined: return "LSBForControl30_Undefined";
		case ControlChangeType_LSBForControl31_Undefined: return "LSBForControl31_Undefined";
		case ControlChangeType_DamperPedalOnOff: return "DamperPedalOnOff";
		case ControlChangeType_PortamentoOnOff: return "PortamentoOnOff";
		case ControlChangeType_SostenutoOnOff: return "SostenutoOnOff";
		case ControlChangeType_SoftPedalOnOff: return "SoftPedalOnOff";
		case ControlChangeType_LegatoFootswitch: return "LegatoFootswitch";
		case ControlChangeType_Hold: return "Hold";
		case ControlChangeType_SoundController1_SoundVariation: return "SoundController1_SoundVariation";
		case ControlChangeType_SoundController2_TimbreHarmonicIntens: return "SoundController2_TimbreHarmonicIntens";
		case ControlChangeType_SoundController3_ReleaseTime: return "SoundController3_ReleaseTime";
		case ControlChangeType_SoundController4_AttackTime: return "SoundController4_AttackTime";
		case ControlChangeType_SoundController5_Brightness: return "SoundController5_Brightness";
		case ControlChangeType_SoundController6_DecayTime: return "SoundController6_DecayTime";
		case ControlChangeType_SoundController7_VibratoRate: return "SoundController7_VibratoRate";
		case ControlChangeType_SoundController8_VibratoDepth: return "SoundController8_VibratoDepth";
		case ControlChangeType_SoundController9_VibratoDelay: return "SoundController9_VibratoDelay";
		case ControlChangeType_SoundController10_Undefined: return "SoundController10_Undefined";
		case ControlChangeType_GeneralPurposeController5: return "GeneralPurposeController5";
		case ControlChangeType_GeneralPurposeController6: return "GeneralPurposeController6";
		case ControlChangeType_GeneralPurposeController7: return "GeneralPurposeController7";
		case ControlChangeType_GeneralPurposeController8: return "GeneralPurposeController8";
		case ControlChangeType_PortamentoControl: return "PortamentoControl";
		case ControlChangeType_HighResolutionVelocityPrefix: return "HighResolutionVelocityPrefix";
		case ControlChangeType_Effects1Depth: return "Effects1Depth";
		case ControlChangeType_Effects2Depth_TremoloDepth: return "Effects2Depth_TremoloDepth";
		case ControlChangeType_Effects3Depth: return "Effects3Depth";
		case ControlChangeType_Effects4Depth_CelesteDetuneDepth: return "Effects4Depth_CelesteDetuneDepth";
		case ControlChangeType_Effects5Depth_PhaserDepth: return "Effects5Depth_PhaserDepth";
		case ControlChangeType_DataIncrement_DataEntryPlus1: return "DataIncrement_DataEntryPlus1";
		case ControlChangeType_DataDecrement_DataEntryMinus1: return "DataDecrement_DataEntryMinus1";
		case ControlChangeType_NonRegistered_ParameterNumberLSB: return "NonRegistered_ParameterNumberLSB";
		case ControlChangeType_NonRegistered_ParameterNumberMSB: return "NonRegistered_ParameterNumberMSB";
		case ControlChangeType_Registered_ParameterNumberLSB: return "Registered_ParameterNumberLSB";
		case ControlChangeType_Registered_ParameterNumberMSB: return "Registered_ParameterNumberMSB";
		case ControlChangeType_AllSoundOff: return "AllSoundOff";
		case ControlChangeType_ResetAllController: return "ResetAllController";
		case ControlChangeType_LocalControl: return "LocalControl";
		case ControlChangeType_AllNoteOff: return "AllNoteOff";
		case ControlChangeType_OmniOn: return "OmniOn";
		case ControlChangeType_OmniOff: return "OmniOff";
		case ControlChangeType_PolyphonicOn: return "PolyphonicOn";
		case ControlChangeType_MonoPhonicOn: return "MonoPhonicOn";
		default: return "Undefined";
		}
	}

	uint8_t MidiMessage::GetControlBitOfChannelModeMessage( Type type )
	{
		return ( type & 0xff00 ) >> 8;
	}

	int MidiMessage::GetDataByteCount( Type type )
	{
		switch ( type )
		{
		case Type_NoteOn:
		case Type_NoteOff:
		case Type_PolyphonicKeyPressure:
		case Type_ControlChange:
		case Type_PitchBendChange:
		case Type_AllSoundOff:
		case Type_ResetAllController:
		case Type_LocalControl:
		case Type_AllNoteOff:
		case Type_OmniOn:
		case Type_OmniOff:
		case Type_PolyphonicOn:
		case Type_MonophonicOn:
		case Type_SongPositionPointer:
			return 2;

		case Type_ProgramChange:
		case Type_ChannelPressure:
		case Type_QuarterFrame:
			return 1;

		case Type_Exclusive:
		case Type_Start:
		case Type_Continue:
		case Type_Stop:
		case Type_ActiveSencing:
		case Type_SystemReset:
		case Type_TimingClock:
		case Type_EndOfExclusive:
		case Type_SongSelect:
		case Type_TuneRequest:
		default:
			return 0;
		}
	}

	bool MidiMessage::IsChannelMessage( Type type )
	{
		if ( ( type & 0xF0 ) == 0xF0 )
		{
			return false;
		}
		return true;
	}

	bool MidiMessage::IsSystemMessage( Type type )
	{
		return not IsChannelMessage( type );
	}

	MidiMessage::Type MidiMessage::GetChannelModeMessageType( uint8_t firstDataByte )
	{
		switch ( Type( firstDataByte ) )
		{
		case MidiMessage::Type_AllSoundOff:
			return Type_AllSoundOff;
		case MidiMessage::Type_ResetAllController:
			return Type_ResetAllController;
		case MidiMessage::Type_LocalControl:
			return Type_LocalControl;
		case MidiMessage::Type_AllNoteOff:
			return Type_AllNoteOff;
		case MidiMessage::Type_OmniOn:
			return Type_OmniOn;
		case MidiMessage::Type_OmniOff:
			return Type_OmniOff;
		case MidiMessage::Type_PolyphonicOn:
			return Type_PolyphonicOn;
		case MidiMessage::Type_MonophonicOn:
			return Type_MonophonicOn;
		default:
			return Type_ControlChange;
		}
	}

	MidiMessage::MidiMessage()
		: type( Type_Unknown )
	{}

	MidiMessage::MidiMessage( Type type )
		: type( type )
	{}

	MidiMessage::MidiMessage( Type type, uint8_t firstDataByte )
		: type( type )
	{
		dataBytes[ 0 ] = firstDataByte;
	}

	MidiMessage::MidiMessage( Type type, uint8_t firstDataByte, uint8_t secondDataByte )
		: type( type )
	{
		dataBytes[ 0 ] = firstDataByte;
		dataBytes[ 1 ] = secondDataByte;
	}


	MidiMessage::FirstCategory MidiMessage::GetFirstCategory() const
	{
		switch ( type )
		{
		case Type_NoteOn:
		case Type_NoteOff:
		case Type_PolyphonicKeyPressure:
		case Type_ControlChange:
		case Type_ProgramChange:
		case Type_ChannelPressure:
		case Type_PitchBendChange:
		case Type_AllSoundOff:
		case Type_ResetAllController:
		case Type_LocalControl:
		case Type_AllNoteOff:
		case Type_OmniOn:
		case Type_OmniOff:
		case Type_PolyphonicOn:
		case Type_MonophonicOn:
			return FirstCategory_Channel;

		case Type_Exclusive:
		case Type_QuarterFrame:
		case Type_SongPositionPointer:
		case Type_SongSelect:
		case Type_TuneRequest:
		case Type_EndOfExclusive:
		case Type_TimingClock:
		case Type_Start:
		case Type_Continue:
		case Type_Stop:
		case Type_ActiveSencing:
		case Type_SystemReset:
			return FirstCategory_System;

		default:
			return FirstCategory_Unknown;
		}
	}

	MidiMessage::SecondCategory MidiMessage::GetSecondCategory() const
	{
		switch ( type )
		{
		case Type_NoteOn:
		case Type_NoteOff:
		case Type_PolyphonicKeyPressure:
		case Type_ControlChange:
		case Type_ProgramChange:
		case Type_ChannelPressure:
		case Type_PitchBendChange:
			return SecondCategory_Voice;

		case Type_AllSoundOff:
		case Type_ResetAllController:
		case Type_LocalControl:
		case Type_AllNoteOff:
		case Type_OmniOn:
		case Type_OmniOff:
		case Type_PolyphonicOn:
		case Type_MonophonicOn:
			return SecondCategory_Mode;

		case Type_Exclusive:
			return SecondCategory_Exclusive;

		case Type_QuarterFrame:
		case Type_SongPositionPointer:
		case Type_SongSelect:
		case Type_TuneRequest:
		case Type_EndOfExclusive:
			return SecondCategory_Common;
	
		case Type_TimingClock:
		case Type_Start:
		case Type_Continue:
		case Type_Stop:
		case Type_ActiveSencing:
		case Type_SystemReset:
			return SecondCategory_RealTime;

		default:
			return SecondCategory_Unknown;
		}
	}

	std::string MidiMessage::ToString( int tabCount, bool varbose ) const
	{
		std::string tabs;
		for ( int i = 0; i < tabCount; i++ )
		{
			tabs.append( "\t" );
		}

		std::stringstream ss;
		if ( varbose )
		{
			ss << tabs << "MidiMessage:" << GetFirstCategoryName( GetFirstCategory() ) << " "
				<< GetSecondCategoryName( GetSecondCategory() ) << " " 
				<< GetTypeName( GetType() ) << " channelId:" << GetChannelId() 
				<< ", data: " << std::hex << Get1stDataByte() << " " << std::hex << Get2ndDataByte() << "\n";
		}
		else
		{
			ss << tabs << "MidiMessage:" << GetTypeName( GetType() ) << " channelId: " << (int) GetChannelId() 
				<< ", data: " << std::hex << (int) Get1stDataByte() << " " << std::hex << (int) Get2ndDataByte();
		}

		return ss.str();
	}

}
